package com.jic.member.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.jic.member.constant.consist.ResponseCode;
import com.jic.member.constant.enums.NodeTemplateTypeEnum;
import com.jic.member.constant.enums.TripTimingJobStatusEnum;
import com.jic.member.constant.enums.TripTimingJobTypeEnum;
import com.jic.member.constant.enums.WaitePeriodEnum;
import com.jic.member.exception.AppRuntimeException;
import com.jic.member.mapper.TripBranchNodeEntityMapper;
import com.jic.member.mapper.TripNodeTemplateEntityMapper;
import com.jic.member.mapper.TripTimingJobEntityMapper;
import com.jic.member.entity.TripBranchNodeEntity;
import com.jic.member.entity.TripNodeTemplateEntity;
import com.jic.member.entity.TripTimingJobEntity;
import com.jic.member.service.TripTimingJobService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.stereotype.Service;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

/**
 * @author yinhaijun
 * @date: 2020/6/5
 */
@Slf4j
@Service
public class TripTimingJobServiceImpl extends ApplicationObjectSupport implements TripTimingJobService {

    public static final String SEPARATE_SIGN = "___";
    private static final List<Integer> CAN_PROCESS_CONDITION = Lists.newArrayList(0, 1);
    @Autowired
    private TripTimingJobEntityMapper tripTimingJobEntityMapper;

    @Autowired
    private TripNodeTemplateEntityMapper tripNodeTemplateEntityMapper;

    @Autowired
    private TripBranchNodeEntityMapper tripBranchNodeEntityMapper;

    @Override
    public void register(TripTimingJobTypeEnum type, String nodeTemplateCode, Long branchNodeId, Date executeTime, String jobDesc, Object ... args) {
        log.info("注册Job开始，branchNodeId = {}", branchNodeId);
        // 如果为旅程启动类型，优先采用已存在数据进行更新操作，避免重复数据
        TripTimingJobEntity tripTimingJobEntity = null;
        if(type == TripTimingJobTypeEnum.TRIP_START) {
            TripTimingJobEntity tripTimingJobEntityQ = new TripTimingJobEntity();
            tripTimingJobEntityQ.setType(type.getCode());
            tripTimingJobEntityQ.setBranchNodeId(branchNodeId);
            tripTimingJobEntity = tripTimingJobEntityMapper.query(tripTimingJobEntityQ);
            // 不是待执行的数据不允许更新，避免数据错误
            if(null != tripTimingJobEntity && !TripTimingJobStatusEnum.WAIT_EXECUTE.getCode().equals(tripTimingJobEntity.getStatus())){
                throw new AppRuntimeException(ResponseCode.EXCEPTION, "不是待执行的数据不允许更新");
            }
        }
        if(null == tripTimingJobEntity){
            tripTimingJobEntity = new TripTimingJobEntity();
        }

        tripTimingJobEntity.setType(type.getCode());
        tripTimingJobEntity.setNodeTemplateCode(nodeTemplateCode);
        tripTimingJobEntity.setStatus(TripTimingJobStatusEnum.WAIT_EXECUTE.getCode());
        tripTimingJobEntity.setJobDesc(jobDesc);
        if(null == executeTime){
            tripTimingJobEntity.setExecuteTime(new Date());
        }else {
            tripTimingJobEntity.setExecuteTime(executeTime);
        }
        tripTimingJobEntity.setBranchNodeId(branchNodeId);
        tripTimingJobEntity.setCreateTime(new Date());
        tripTimingJobEntity.setUpdateTime(tripTimingJobEntity.getCreateTime());
        if(null != args || 0 != args.length){
            StringBuffer requestVariableBuffer = new StringBuffer();
            for(int i = 0; i < args.length; i++){
                String argStr = null;
                // 这里转化需要谨慎，传入的是对象，接收时也有可能转化成json了，未查明原因
                try{
                    if(args[i] instanceof String){
                        argStr = (String)args[i];
                    }else {
                        argStr = JSON.toJSONString(args[i]);
                    }
                }catch (Exception e){
                    log.error("入参转JSON失败");
                    throw new AppRuntimeException(ResponseCode.EXCEPTION, "入参转JSON失败");
                }
                requestVariableBuffer.append(argStr);
                if(i != args.length - 1){
                    requestVariableBuffer.append(SEPARATE_SIGN);
                }
            }
            tripTimingJobEntity.setRequestVariable(requestVariableBuffer.toString());

        }
        if(null == tripTimingJobEntity.getId() || tripTimingJobEntity.getId().equals(0L)){
            tripTimingJobEntityMapper.insert(tripTimingJobEntity);
        }else {
            tripTimingJobEntityMapper.update(tripTimingJobEntity);
        }
        log.info("注册Job结束, branchNodeId = {}", branchNodeId);
        // 如果是立即执行，异步调用执行方法
        if(null == executeTime){
            TripTimingJobEntity finalTripTimingJobEntity = tripTimingJobEntity;
            new Thread(() -> execute(finalTripTimingJobEntity)).start();
        }
    }

    @Override
    public void exeNext(Long nowNodeId, List<String> userIds) {
        // 查询出下一个节点
        TripBranchNodeEntity tripBranchNodeEntityQN = new TripBranchNodeEntity();
        tripBranchNodeEntityQN.setPrevBranchNodeId(nowNodeId);
        tripBranchNodeEntityQN.setDeleteFlag(0);
        List<TripBranchNodeEntity> tripBranchNodeEntityList = tripBranchNodeEntityMapper.findAll(tripBranchNodeEntityQN);
        if(null != tripBranchNodeEntityList && !tripBranchNodeEntityList.isEmpty()){
            tripBranchNodeEntityList.stream()
                    .filter(t->{
                        return !t.getNodeTemplateCode().equals("event_judge_node") && CAN_PROCESS_CONDITION.contains(t.getBranchCondition()) ;
                    }).forEach(entity ->{
                Date date = new Date() ;
                if(entity.getNodeTemplateCode().equals(NodeTemplateTypeEnum.WAIT_NODE.getCode())){
                        String nodeVariable = entity.getNodeVariable();
                        // 解析json
                        JSONObject jsonObject = JSONObject.parseObject(nodeVariable);
                        Integer waitType = (Integer) jsonObject.get("waitType");
                        if(waitType.equals(1)){//直接触发
                            date = new Date();
                        }else {
                            String waitTimeStr = (String) jsonObject.get("waitTime");
                            Integer waitTime = Integer.valueOf(waitTimeStr);
                            Integer waitPeriod = (Integer) jsonObject.get("waitPeriod");
                            if(WaitePeriodEnum.MINUTE.getCode().equals(waitPeriod)){
                                date = new Date(date.getTime() + waitTime * waitPeriod * 60 * 1000);
                            }
                            else if(WaitePeriodEnum.HOUR.getCode().equals(waitPeriod)){
                                date = new Date(date.getTime() + waitTime * waitPeriod * 60 * 60 * 1000);
                            }
                            else if(WaitePeriodEnum.DAY.getCode().equals(waitPeriod)){
                                date = new Date(date.getTime() + waitTime * waitPeriod *24 * 60 * 60 * 1000);
                            }
                            else if(WaitePeriodEnum.WEEK.getCode().equals(waitPeriod)){
                                date = new Date(date.getTime() + waitTime * waitPeriod * 7 * 24 * 60 * 60 * 1000);
                            }
                            else if(WaitePeriodEnum.WEEK.getCode().equals(waitPeriod)){
                                date = new Date(date.getTime() + waitTime * waitPeriod * 30 * 7 * 24 * 60 * 60 * 1000);
                            }
                        }
                    }
                register(TripTimingJobTypeEnum.EXE_ACTION, entity.getNodeTemplateCode(), entity.getId(), date,
                        NodeTemplateTypeEnum.getByCode(entity.getNodeTemplateCode()).getMessage(), entity.getTripId(), entity.getId(), userIds);
            });
        }


    }

    @Override
    public void process() {
        // 查询表中状态为待执行的数据
        List<TripTimingJobEntity> tripTimingJobEntityList = tripTimingJobEntityMapper.findByStatusAndLessThanExecuteTime(TripTimingJobStatusEnum.WAIT_EXECUTE.getCode(), new Date());
        if(null == tripTimingJobEntityList || tripTimingJobEntityList.isEmpty()){
            return;
        }
        for(TripTimingJobEntity tripTimingJobEntity : tripTimingJobEntityList){
            execute(tripTimingJobEntity);
        }
    }

    @Override
    public void close(Long tripId) {
        // 根据tripId查询所有节点的id
        // 关闭节点的id
//        tripTimingJobEntityMapper.close();
    }

    private void execute(TripTimingJobEntity tripTimingJobEntity){
        try {
            // 设置执行状态为处理中并保存，直接提交事务，避免其他线程查出此条数据
            tripTimingJobEntity.setStatus(TripTimingJobStatusEnum.EXECUTEING.getCode());
            tripTimingJobEntityMapper.update(tripTimingJobEntity);

            TripNodeTemplateEntity tripNodeTemplateEntityQ = new TripNodeTemplateEntity();
            tripNodeTemplateEntityQ.setTemplateCode(tripTimingJobEntity.getNodeTemplateCode());
            TripNodeTemplateEntity tripNodeTemplateEntity = tripNodeTemplateEntityMapper.query(tripNodeTemplateEntityQ);
            String executeMethod = tripNodeTemplateEntity.getExecuteMethod();
            String[] executeMethods = executeMethod.split(SEPARATE_SIGN);
            // 获取服务名，Spring管理的beanname
            String processorClassName = executeMethods[0];
            // 方法名
            String processorMethodName = executeMethods[1];

             // 参数类名
            Class[] parmClasses = new Class[executeMethods.length - 2];
            for(int i = 2; i < executeMethods.length; i++){
                parmClasses[i-2] = Class.forName(executeMethods[i]);
            }

            // 参数具体取值
            String requestVariable = tripTimingJobEntity.getRequestVariable();
            Object[] args;
            if(null == requestVariable || "".equals(requestVariable)){
                args = new Object[0];
            }else {
                String[] requestVariables = requestVariable.split(SEPARATE_SIGN);
                args = new Object[requestVariables.length];
                for (int i = 0; i < requestVariables.length; i++) {
                    args[i] = JSON.parseObject(requestVariables[i], parmClasses[i]);
                }
            }

            //从Spring容器中获取对应的服务
            Object discountProcessor = getApplicationContext().getBean(processorClassName);
            // 反射获取方法
            Method method = discountProcessor.getClass().getMethod(processorMethodName, parmClasses);
            method.setAccessible(true);
            log.info("定时任务{}{}调用入参：{}", tripTimingJobEntity.getJobDesc(), tripTimingJobEntity.getId(), tripTimingJobEntity.getRequestVariable());
            long start = new Date().getTime();
            Object result = method.invoke(discountProcessor, args);
            log.info("定时任务{}{}调用结果：{},耗时{}", tripTimingJobEntity.getJobDesc(), tripTimingJobEntity.getId(), JSON.toJSONString(result), start - new Date().getTime());
            // 执行完毕更新状态为执行成功
            tripTimingJobEntity.setResponseVariable(JSON.toJSONString(result));
            tripTimingJobEntity.setStatus(TripTimingJobStatusEnum.ALREADY_EXECUTE.getCode());
            tripTimingJobEntity.setUpdateTime(new Date());
            tripTimingJobEntityMapper.update(tripTimingJobEntity);
            // 执行成功后，需要在对应的表中记录用户状态
        } catch (InvocationTargetException e) {
            // 出现异常修改状态
            log.error("定时任务{}调用服务失败", tripTimingJobEntity.getId());
            Throwable t = e.getTargetException();
            tripTimingJobEntity.setResponseVariable(getStackTrace(t));
            tripTimingJobEntity.setStatus(TripTimingJobStatusEnum.FAIL_EXECUTE.getCode());
            tripTimingJobEntity.setUpdateTime(new Date());
            tripTimingJobEntityMapper.update(tripTimingJobEntity);
        } catch (Exception e){
            tripTimingJobEntity.setResponseVariable(getStackTrace(e));
            tripTimingJobEntity.setStatus(TripTimingJobStatusEnum.FAIL_EXECUTE.getCode());
            tripTimingJobEntity.setUpdateTime(new Date());
            tripTimingJobEntityMapper.update(tripTimingJobEntity);
        }
    }

    /**
     * 将异常堆栈转换为字符串
     * @param aThrowable 异常
     * @return String
     */
    public static String getStackTrace(Throwable aThrowable) {
        final Writer result = new StringWriter();
        final PrintWriter printWriter = new PrintWriter(result);
        aThrowable.printStackTrace(printWriter);
        return result.toString();
    }

}
